<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    /* 
     一、事件触发三阶段
    ①事件捕获阶段
    ②目标阶段（目标对象处理的函数）
    ③冒泡阶段

    二、注册事件
    ①addEventListener(type,function,useCapture）--> removeEventListener
    @param {*} type 事件类型
    @param {*} function 事件处理程序的函数
    @param {*} useCapture 默认为false false表示在冒泡阶段调用时间处理程序 
    true表示在捕获阶段调用时间处理程序
    ②IE
    attachEvent(type,function) --> detachEvent
    @param {*} type 事件类型
    @param {*} function 事件处理程序的函数
    ie8及更早版本只支持事件冒泡，所以事件处理程序的函数在冒泡阶段调用
    ie DOM0级事件处理程序不在所属元素的作用域内运行 DOM2级事件是全局作用域 this=window 
    并且不按添加顺序执行 反着的

    三、事件委托
    利用冒泡的原理把子元素的事件委托给父元素 整个页面减少内存占用 不需要给子节点注销事件
    ul.addEventListener('click', (e) => {
        var e  = e || window.e
        var target = e.target || e.srcElement
		console.log(event.target);
         if(target == LI){
            alert(1);
        }
	})
    四、跨域
    同源策略：协议，域名，端口一致 浏览器出于安全考虑，不符合同源策略Ajax请求会失败
    注：其实主要是用来防止 CSRF 攻击的。简单点说，CSRF 攻击是利用用户的登录态发起恶意请求。
    请求跨域了，那么请求到底发出去没有？请求发出去了，只是被浏览器拦截，所以也并不能阻止攻击
    ①JSONP 添加<script>标签来调用服务器提供的js脚本。
    利用 <script> 标签没有跨域限制的漏洞。
    通过 <script> 标签指向一个需要访问的地址并提供一个回调函数来接收数据
    function jsonp(url, jsonpCallback, success) {
      let script = document.createElement('script')
      script.src = url
      script.async = true
      script.type = 'text/javascript'
      // 创建全局回调函数
      window[jsonpCallback] = function(data) {
          success && success(data)
      }
      document.body.appendChild(script)
    }
    jsonp('http://xxx', 'callback', function(value) {
      console.log(value)
    })
    JSONP 使用简单且兼容性不错，但是只限于 get 请求。
    ②CORS
    使用CORS跨域，会在发送请求时出现两种情况，分别为简单请求和复杂请求。
    1、简单请求：满足以下两个条件
    ①请求方法是以下方法之一
    get head post
    ②http头信息不超过以下几种字段
    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
    Content-Type：只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
     //配置cors中间件
    app.use(cors({
        "origin": ["http://localhost:8005","http://localhost:8008"],  //允许所有前端域名
        "credentials":true,
    //允许携带凭证，表示是否允许发送Cookie，如果服务器不要浏览器发送Cookie，删除该字段即可。
    // 另一方面，开发者必须在AJAX请求中打开withCredentials属性。
        "methods": "GET,HEAD,PUT,PATCH,POST,DELETE", //被允许的提交方式
        "allowedHeaders":['Content-Type','Authorization']//被允许的post方式的请求头
    }));
注：如果要发送Cookie，Access-Control-Allow-Origin就不能设为星号，必须指定明确的、与请求网页一致的域名。
同时，Cookie依然遵循同源政策，只有用服务器域名设置的Cookie才会上传，其他域名的Cookie并不会上传，
且（跨源）原网页代码中的document.cookie也无法读取服务器域名下的Cookie。
   
    2、凡不满足以上条件的即为非简单请求
    比如请求方法是PUT或DELETE，或者Content-Type字段的类型是application/json
    1.浏览器发起预检请求，请求方法是OPTIONS

    CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器，以及可以向不支持CORS的网站请求数据。
    ③document.domain
    该方式只能用于二级域名相同的情况下，比如 a.test.com 和 b.test.com 适用于该方式。
    只需要给页面添加 document.domain = 'test.com' 表示二级域名都相同就可以实现跨域

    ④postMessage
    这种方式通常用于获取第三方页面数据。一个页面发送消息，另一个页面判断来源并接收消息
    // 发送消息端
    window.parent.postMessage('message', 'http://test.com')
    // 接收消息端
    var mc = new MessageChannel()
    mc.addEventListener('message', event => {
      var origin = event.origin || event.originalEvent.origin
      if (origin === 'http://test.com') {
        console.log('验证通过')
      }
    })
    
    五、存储
    cookie:由服务器生成，可以设置过期时间，携带在请求头中 4k
    localStorage，sessionStorage,indexDB 不参与与服务器通信，大小分别为5M,5M,无限大
    sessionStorage 会话级 关闭页面就清理  其他二者除非手动清理不然一直存在

    cookie安全性：
    value尽量不使用明文 加密，
    不能通过JS访问Cookie，减少 XSS 攻击，
    只能在协议为 HTTPS 的请求中携带
    规定浏览器不能在跨域请求中携带 Cookie，减少CSRF攻击

    Service Worker
    传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截，所以必须使用 HTTPS 协议来保障安全。
   ① 注册 Service Worker
   ②监听到 install 事件以后就可以缓存需要的文件，
   ③在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存，存在缓存的话就可以直接读取，否则就去请求数据。

   六、浏览器缓存机制（显著减少网络传输所带来的损耗）
   一个请求分为网络请求 后端处理 浏览器响应三个步骤，浏览器缓存机制可以优化一 三步骤，
   直接缓存不请求,或者请求但后端和前端一致，就不传回数据，减少响应数据
   ①缓存位置
   1.Service Worker--->2.Memory Cache (内存中的缓存,关闭进程释放)--->3.Disk Cache（硬盘缓存，时效和容量比前者好）
   --->4.Push Cache（会话级，http/2中）--->5.网络请求（无缓存情况下）
   ②缓存策略
   通常浏览器缓存策略分为两种：强缓存和协商缓存，并且缓存策略都是通过设置 HTTP Header 来实现的。
   1.强缓存（强缓存表示在缓存期间不需要请求，state code 为 200。）
   expires:设置过期时间，会受限于本地时间，若修改本地时间，会造成缓存失效
   Cache-control(请求头):优先于expires，设置过期时间max-age = 30 30秒之后过期。
   可以在请求头或者响应头中设置，并且可以组合使用多种指令：public/private/max-age=30/no-cache,max-stale=30
   2.协商缓存
   Last-Modified（响应头）: 表示本地文件最后修改日期 
   If-Modified-Since （请求头）会将 Last-Modified 的值发送给服务器，询问服务器在该日期后资源是否有更新，
   有更新的话就会将新的资源发送回来，否则返回 304 状态码。
   弊端: 打开会认为是最后修改时间，重复返回，只能以秒计时 ，在秒以内的时间修改判断不到
   Etag（响应头）:
   请求头:If-None-Match Etag和If-None-Match如果相等，即返回304
   etag如何添加?根据文件内容,生成一个md5的摘要，给实体加一个标签。
  ETag 类似于文件指纹，If-None-Match 会将当前 ETag 发送给服务器，
  询问该资源 ETag 是否变动，有变动的话就将新的资源发送回来。并且 ETag 优先级比 Last-Modified 高。
  3.常见的 HTTP 缓存只能存储 GET 响应（一般只有GET请求才会被缓存）
   Cache-Control

  七、浏览器渲染原理
  1.DNS 查询 ---》2.TCP 连接 ---》3.HTTP 请求即响应 ---》4.服务器响应 ---》5.客户端渲染

  1.处理html标记并构建dom树
  2.处理css标记并构建cssom树（CSS Object Model）
  3.将html和css合并成一个渲染树
  4.根据渲染树来布局（layout），即将所有渲染树的所有节点进行平面合成
  5.将各个节点绘制到屏幕上（paint）

  ①如果 DOM 或 CSSOM 被修改，您只能再执行一遍以上所有步骤，以确定哪些像素需要在屏幕上进行重新渲染。
  ②当浏览器遇到一个 script 标记时，DOM 构建将暂停，直至脚本完成执行，避免阻塞，加async，但依旧会阻塞load事件 
  动态添加的script标签 默认async 不会阻塞加载页面
  ③link标签会阻塞加载
  ④重绘和回流（重排）重排的代价大
  重排：重新生成布局
  重绘：重新绘制
  优化：①不要一条一条改变样式，而是通过class一次性改变
       ②频繁操作的话 用visibility:hidden替换display:none
       ③前端框架 虚拟dom

    1.插入几万个 DOM，如何实现页面不卡顿？
    ①通过 requestAnimationFrame 的方式去循环的插入 DOM
    ②虚拟滚动，只渲染可视区域内的内容，非可见区域的那就完全不渲染了，当用户在滚动的时候就实时去替换渲染的内容。
  
  八、浏览器安全问题
  ①xss攻击 转义字符，白名单过滤的方法
  ②CSP攻击
  ③CSRF 跨站伪造请求 攻击者构造出一个后端请求地址，诱导用户点击或者通过某些途径自动发起请求。
  如果用户是在登录状态下的话，后端就以为是用户在操作，从而进行相应的逻辑。
  1.Get 请求不对数据进行修改
  2.不让第三方网站访问到用户 Cookie
  3.阻止第三方网站请求接口
  4.请求时附带验证信息，比如验证码或者 Token
  ④点击劫持（通过 iframe 嵌套的方式嵌入自己的网页中，并将 iframe 设置为透明，在页面中透出一个按钮诱导用户点击。）
  1. HTTP 响应头X-FRAME-OPTIONS，指定iframe 的展示方式
  2.js防御 当通过 iframe 的方式加载页面时，攻击者的网页直接不显示所有内容了
  ⑤中间人劫持 （攻击方同时与服务端和客户端建立起了连接）
  不要连接公共不安全wifi 全部用https请求

  九、互联网知识
  实体层 （把电脑连接起来）---> 链接层 -->网络层（电脑有了两个地址：mac地址和网络地址）
   --->传输层（建立通信：UDP协议--无法知道对方是否收到和TCP协议--确保数据不会遗失） --->应用层

   UDP协议：
   ①面向无连接，即不需要连接双方再传送数据，不保证数据不丢失传输给对方，不需要验证数据报文，不需要流量控制
   ②高效。实时性高：直播，王者荣耀
   TCP协议：
   ①确保数据不丢失
   ②三次握手 确保连接
   1.客户端向服务端发送连接请求报文段 
   2.服务端收到连接请求报文段后，同意连接，则发送应答
   3.当客户端收到连接同意的应答后，还要向服务端发送一个确认报文
   注：第三次握手是为了防止无效的请求报文被服务器接收，从而产生错误
   断开四次握手TCP 是全双工的，在断开连接时两端都需要发送 FIN 和 ACK。
   http:
   ①无状态协议 不持久
   http/2:
   ①二进制传输 文本-》二进制
   ②多路复用
   ③header压缩
   ④服务端push


   十、设计模式
   ①工厂模式
   



     */
     var eventUtil = {
        addHandler: function(element, type, handler){
            if(element.addEventListener){
                element.addEventListener(type,handler,false)
            } else if (element.attachEvent){
                element.attachEvent("on"+type,handler)
            } else{
                element["on"+type] = handler
            }
        },
        removeHandler: function(element, type, handler){
            if(element.addEventListener){
                element.removeEventListener(type,handler,false)
            } else if (element.attachEvent){
                element.detachEvent("on"+type,handler)
            } else{
                element["on"+type] = null
            }
        },
        getEvent(e){
            return e ? e : window.event
        },
        getTarget(e){
            return e.target || e.srcElement
        },
        preventDefault(e){
            if(e.preventDefault){
                e.preventDefault()
            } else {
                e.returnValue = false
            }
        },
        stopProgation(e){
            if(e.stopProgation){
                e.stopProgation()//取消事件冒泡货进一步捕获 stopImmediatePropagetion()阻止冒泡和调用 dom3新增
            } else {
                e.cancelBubble = true
            }
        }
     }
 // 工厂模式: 避免创建多个对象，用户不关心逻辑，参数使用,只需要返回给我一个实例对象，提供接口给我调用
     //传统
     function createPerson (name){
       const obj = new Object() //原料
       obj.name = name  //加工
       obj.sayName = function () {
         console.log(this.na )
       }
       return obj //出厂
     } 
     var p1 = createPerson('zz')
     var p2 = createPerson('xx')

    //  js内置
     function createPerson (name){
       this.name = name
       this.sayName = function () {
         console.log(this.name)
       }
     } 
     var p1 = new createPerson('zz')
     var p2 = new createPerson('xx')

     p1 === p2 //false 
     p1.sayName === p2.sayName //false 产生多个相同函数


  //设计模式:单例模式：每次new出来的是同一个实例
		
		function Fn(){
			Fn.obj = {name:"admin"};
			return Fn;
		}
		var obj1 = new Fn();
		var obj2 = new Fn();
		
		console.log(obj1==obj2);			//true
		console.log(obj1.obj.name === obj2.obj.name);//admin true
// 必须的有一个全局变量
// 必须return同一个对象
// 单例模式的应用: 确保无论调用多少次toast方法，都只创建一个DOM元素
		
//		模态框,点击按钮,显示提示框,多次点击按钮,显示同一个提示框
        var obtn = document.getElementById("btn");
		
		function Toast(){
			if(!Toast.div){
				Toast.div = document.createElement("div");
				Toast.div.className = "box";
				document.body.appendChild(Toast.div);
				clearTimeout(Toast.timer);
				Toast.timer = setTimeout(function(){
					Toast.div.style.display = "none";
				},1000)
			}else{
				Toast.div.style.display = "block";
				clearTimeout(Toast.timer);
				Toast.timer = setTimeout(function(){
					Toast.div.style.display = "none";
				},1000)
			}
			return Toast;
		}
			
		obtn.onclick = function(){
			var a = new Toast();
			var b = new Toast();
			console.log(a == b)
		}
	// 发布订阅模式:点击一个按钮触发了点击事件;
  // 在 Vue 中，实现响应式也是使用了该模式。对于需要实现响应式的对象来说，在 get 的时候会进行依赖收集，
  // 当改变了对象的属性时，就会触发派发更新。
  // 代理模式:事件委托就是代理模式 
</script>    
</body>
</html>